home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 2 / Atari Mega Archive CD - Volume 2.iso / minix / up1510b.tgz / up1510b / src / commands / grep.c < prev    next >
C/C++ Source or Header  |  1990-07-23  |  7KB  |  264 lines

  1. /* grep - search a file for a pattern    Author: Norbert Schlenker */
  2.  
  3. /* Norbert Schlenker (nfs@princeton.edu)  1990-02-08
  4.  * Released into the public domain.
  5.  *
  6.  * Grep searches files for lines containing a pattern, as specified by
  7.  * a regular expression, and prints those lines.  It is invoked by:
  8.  *    grep [flags] [pattern] [file ...]
  9.  *
  10.  * Flags:
  11.  *    -e pattern    useful when pattern begins with '-'
  12.  *    -l        prints just file names, no lines (quietly overrides -n)
  13.  *    -n        printed lines are preceded by relative line numbers
  14.  *    -s        prints errors only (quietly overrides -l and -n)
  15.  *    -v        prints lines which don't contain the pattern
  16.  *
  17.  * Semantic note:
  18.  *     If both -l and -v are specified, grep prints the names of those
  19.  *    files which do not contain the pattern *anywhere*.
  20.  *
  21.  * Exit:
  22.  *    Grep sets an exit status which can be tested by the caller.
  23.  *    Note that these settings are not necessarily compatible with
  24.  *    any other version of grep, especially when -v is specified.
  25.  *    Possible status values are:
  26.  *      0    if any matches are found
  27.  *      1    if no matches are found
  28.  *      2    if syntax errors are detected or any file cannot be opened
  29.  */
  30.  
  31.  
  32. /* External interfaces */
  33. #include <regexp.h>        /* Thanks to Henry Spencer */
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <stdio.h>
  37.  
  38. extern int getopt();        /* Thanks to Henry Spencer */
  39. extern char *optarg;
  40. extern int optind;
  41.  
  42.  
  43. /* Internal constants */
  44. #define MATCH        0    /* exit code: some match somewhere */
  45. #define NO_MATCH    1    /* exit code: no match on any line */
  46. #define FAILURE        2    /* exit code: syntax error or bad file name */
  47.  
  48.  
  49. /* Macros */
  50. #define SET_FLAG(c)    (flags[(c)-'a'] = 1)
  51. #define FLAG(c)        (flags[(c)-'a'] != 0)
  52.  
  53.  
  54. /* Private storage */
  55. static char *program;        /* program name */
  56. static char flags[26];        /* invocation flags */
  57. static regexp *expression;    /* compiled search pattern */
  58.  
  59.  
  60. /* Internal interfaces */
  61. static int match();
  62. static char *get_line();
  63. static void error_exit();
  64.  
  65.  
  66. int main(argc, argv)
  67. int argc;
  68. char *argv[];
  69. {
  70.   int opt;            /* option letter from getopt() */
  71.   char *pattern;        /* search pattern */
  72.   int exit_status = NO_MATCH;    /* exit status for our caller */
  73.   int file_status;        /* status of search in one file */
  74.   FILE *input;            /* input file (if not stdin) */
  75.  
  76.   program = argv[0];
  77.   memset(flags, 0, sizeof(flags));
  78.   pattern = (char *) NULL;
  79.  
  80. /* Process any command line flags. */
  81.   while ((opt = getopt(argc, argv, "e:lnsv")) != EOF) {
  82.     if (opt == '?')
  83.         exit_status = FAILURE;
  84.     else
  85.     if (opt == 'e')
  86.         pattern = optarg;
  87.     else
  88.         SET_FLAG(opt);
  89.   }
  90.  
  91. /* Detect a few problems. */
  92.   if ((exit_status == FAILURE) || (optind == argc && pattern == (char *) NULL))
  93.     error_exit("Usage: %s [-lnsv] [-e] expression [file ...]\n");
  94.  
  95. /* Ensure we have a usable pattern. */
  96.   if (pattern == (char *) NULL)
  97.     pattern = argv[optind++];
  98.   if ((expression = regcomp(pattern)) == (regexp *) NULL)
  99.     error_exit("%s: bad regular expression\n");
  100.  
  101. /* Process the files appropriately. */
  102.   if (optind == argc) {        /* no file names - find pattern in stdin */
  103.     exit_status = match(stdin, (char *) NULL, "<stdin>");
  104.   }
  105.   else 
  106.   if (optind + 1 == argc) {    /* one file name - find pattern in it */
  107.     if (strcmp(argv[optind], "-") == 0) {
  108.         exit_status = match(stdin, (char *) NULL, "-");
  109.     } else {
  110.         if ((input = fopen(argv[optind], "r")) == (FILE *) NULL) {
  111.             fprintf(stderr, "%s: couldn't open %s\n",
  112.                             program, argv[optind]);
  113.             exit_status = FAILURE;
  114.         }
  115.         exit_status = match(input, (char *) NULL, argv[optind]);
  116.     }
  117.   }
  118.   else
  119.   while (optind < argc) {    /* lots of file names - find pattern in all */
  120.     if (strcmp(argv[optind], "-") == 0) {
  121.         file_status = match(stdin, "-", "-");
  122.     } else {
  123.         if ((input = fopen(argv[optind], "r")) == (FILE *) NULL) {
  124.             fprintf(stderr, "%s: couldn't open %s\n",
  125.                             program, argv[optind]);
  126.             exit_status = FAILURE;
  127.         } else {
  128.             file_status = match(input, argv[optind], argv[optind]);
  129.             fclose(input);
  130.         }
  131.     }
  132.     if (exit_status != FAILURE)
  133.         exit_status &= file_status;
  134.     ++optind;
  135.   }
  136.   exit(exit_status);
  137. }
  138.  
  139.  
  140. /* match - matches the lines of a file with the regular expression.
  141.  * To improve performance when either -s or -l is specified, this
  142.  * function handles those cases specially.
  143.  */
  144.  
  145. static int match(input, label, filename)
  146. FILE *input;
  147. char *label;
  148. char *filename;
  149. {
  150.   char *line;            /* pointer to input line */
  151.   long int lineno = 0;        /* line number */
  152.   int status = NO_MATCH;    /* summary of what was found in this file */
  153.  
  154.   if (FLAG('s') || FLAG('l')) {
  155.     while ((line = get_line(input)) != (char *) NULL)
  156.         if (regexec(expression, line, 1)) {
  157.             status = MATCH;
  158.             break;
  159.         }
  160.     if (FLAG('l'))
  161.         if ((!FLAG('v') && status == MATCH) ||
  162.             ( FLAG('v') && status == NO_MATCH))
  163.             puts(filename);
  164.     return status;
  165.   }
  166.  
  167.   while ((line = get_line(input)) != (char *) NULL) {
  168.     ++lineno;
  169.     if (regexec(expression, line, 1)) {
  170.         status = MATCH;
  171.         if (!FLAG('v')) {
  172.             if (label != (char *) NULL)
  173.                 printf("%s:", label);
  174.             if (FLAG('n'))
  175.                 printf("%ld:", lineno);
  176.             puts(line);
  177.         }
  178.     } else {
  179.         if (FLAG('v')) {
  180.             if (label != (char *) NULL)
  181.                 printf("%s:", label);
  182.             if (FLAG('n'))
  183.                 printf("%ld:", lineno);
  184.             puts(line);
  185.         }
  186.     }
  187.   }
  188.   return status;
  189. }
  190.  
  191.  
  192. /* get_line - fetch a line from the input file
  193.  * This function reads a line from the input file into a dynamically
  194.  * allocated buffer.  If the line is too long for the current buffer,
  195.  * attempts will be made to increase its size to accomodate the line.
  196.  * The trailing newline is stripped before returning to the caller.
  197.  */
  198.  
  199. #define FIRST_BUFFER 256        /* first buffer size */
  200.  
  201. static char *buf = (char *) NULL;    /* input buffer */
  202. static size_t buf_size = 0;        /* input buffer size */
  203.  
  204. static char *get_line(input)
  205. FILE *input;
  206. {
  207.   int n;
  208.   register char *bp;
  209.   register int c;
  210.   char *new_buf;
  211.   size_t new_size;
  212.  
  213.   if (buf_size == 0) {
  214.     if ((buf = (char *) malloc(FIRST_BUFFER)) == (char *) NULL)
  215.         error_exit("%s: not enough memory\n");
  216.     buf_size = FIRST_BUFFER;
  217.   }
  218.  
  219.   bp = buf;
  220.   n = buf_size;
  221.   while (1) {
  222.     while (--n > 0 && (c = getc(input)) != EOF) {
  223.         if (c == '\n') {
  224.             *bp = '\0';
  225.             return buf;
  226.         }
  227.         *bp++ = c;
  228.     }
  229.     if (c == EOF)
  230.         return (ferror(input) || bp == buf) ? (char *) NULL : buf;
  231.     new_size = buf_size << 1;
  232.     if ((new_buf = (char *) realloc(buf, new_size)) == (char *) NULL) {
  233.         fprintf(stderr, "%s: line too long - truncated\n", program);
  234.         while ((c = getc(input)) != EOF && c != '\n') ;
  235.         *bp = '\0';
  236.         return buf;
  237.     } else {
  238.         bp = new_buf + (buf_size - 1);
  239.         n = buf_size + 1;
  240.         buf = new_buf;
  241.         buf_size = new_size;
  242.     }
  243.   }
  244. }
  245.  
  246.  
  247. /* Regular expression code calls this routine to print errors. */
  248.  
  249. void regerror(s)
  250. char *s;
  251. {
  252.   fprintf(stderr, "regexp: %s\n", s);
  253. }
  254.  
  255.  
  256. /* Common exit point for outrageous errors. */
  257.  
  258. static void error_exit(msg)
  259. char *msg;
  260. {
  261.   fprintf(stderr, msg, program);
  262.   exit(FAILURE);
  263. }
  264.